package analizador_sintactico;

import java.io.FileNotFoundException;
import java.io.IOException;
import analizadorlexico.Token;
import analizadorlexico.AnalizadorLexico;
import analizadorlexico.ErrorLexico;
import arbol_sintactico_abstracto.*;
import generador_de_codigo.GeneradorDeCodigo;
import java.util.ArrayList;
import java.util.List;
import tabla_de_simbolos.Clase;
import tabla_de_simbolos.Constructor;
import tabla_de_simbolos.ErrorSemantico;
import tabla_de_simbolos.Metodo;
import tabla_de_simbolos.ModificadorMetodo;
import tabla_de_simbolos.TablaDeSimbolos;
import tabla_de_simbolos.Tipo;
import tabla_de_simbolos.TipoPrimitivo;

public class AnalizadorSintactico {

    AnalizadorLexico analizador;
    String nombre;
    String lexema;
    int linea;
    boolean fin = false;
    TablaDeSimbolos ts;
    Token token,actual;
    

    
    public AnalizadorSintactico(String fuente,String destino) throws FileNotFoundException {
        analizador = new AnalizadorLexico(fuente);
        actual = null;
        GeneradorDeCodigo.crearArchivo(destino);
    }

    
    public void consumirToken() throws IOException, ErrorLexico {
        token = actual;
        actual = analizador.getToken();
        if(actual != null) {
                nombre = actual.getNombre();
                linea = actual.getLinea();
                lexema = actual.getLexema();
        }
        else {     //se termino el archivo
        
                fin = true;
                nombre = "FIN DE ARCHIVO";
                lexema = "FIN DE ARCHIVO";
        }
    }

    public void match(String _nombre) throws IOException, ErrorLexico, ErrorSintactico {
            if(nombre.equals(_nombre)) {
                consumirToken();
            }
            else
                throw new ErrorSintactico("Se esperaba <" + _nombre + ">, pero se encontró el token <"+ nombre + "," + lexema + "> en la línea " + linea);
    }

    public void analizarSintaxis() throws IOException, ErrorLexico, ErrorSintactico, ErrorSemantico {
        consumirToken();
        if(fin)
            throw new ErrorSintactico("El archivo de entrada esta vacío.");
        else{
            edtInicial();
            ListaClases();
            ts.chequeoDeclaraciones();            
            GeneradorDeCodigo.inicializar();
            ts.chequeoSentencias();            
        }
        System.out.println("Compilacion exitosa!");
    }

    private void ListaClases() throws IOException, ErrorLexico, ErrorSintactico, ErrorSemantico { 
        Clase(); 
        ListaClasesF();
    }

    private void ListaClasesF() throws IOException, ErrorLexico, ErrorSintactico, ErrorSemantico {
        if(!fin)				
            ListaClases();
    }
    
    private void Clase()throws IOException, ErrorLexico, ErrorSintactico, ErrorSemantico {
        // class identificador <Herencia> {<ListaMiembro>}
       match("class");
       match("identificador");       
       Herencia(edtClase(token));
       match("llave_abre");
       ListaMiembro();
       match("llave_cierra");

    } 
    
    private void Herencia(Clase clase)throws IOException, ErrorLexico, ErrorSintactico, ErrorSemantico {
        if(nombre.equals("extends"))  {
            match("extends");
            match("identificador");
            if(!ts.getListaClases().containsKey(token.getLexema()))
                ts.getListaClases().put(token.getLexema(), new Clase(token, true));										
            clase.setPadre(ts.getListaClases().get(token.getLexema()));
        }
        else{
            clase.setPadre(ts.getListaClases().get("Object"));
        }
    }

    private void ListaMiembro()throws IOException, ErrorLexico, ErrorSintactico, ErrorSemantico {
        if(nombre.equals("var") || nombre.equals("static") || nombre.equals("dynamic") || nombre.equals("identificador")){
            Miembro();
            ListaMiembro();
        }
        if (!nombre.equals("llave_cierra")) {   //siguiente(ListaMiembro)= }
             throw new ErrorSintactico("Se esperaba <llave_cierra>, <var>, <static>, <dynamic> o <identificador> y se encontro el token <"+ nombre + "," + lexema + "> en la linea " + linea);
        }        
    }
    
    private void Miembro()throws IOException, ErrorLexico, ErrorSintactico, ErrorSemantico {
        // <Miembro> -> <Atributo> | <Ctor> | <Metodo>
        switch (nombre){
        case "var": //primero(Atributo)
            ts.setDefiniendo("clase");
            Atributo();
            break;  
        case "identificador": //primero(Ctor)
            ts.setDefiniendo("constructor");
            Ctor();
            break;  
        case "static": //primero(Metodo)= primero(ModMetodo)
            ts.setDefiniendo("metodo");
            Metodo();
            break;
        case "dynamic": //primero(Metodo)= primero(ModMetodo)
            ts.setDefiniendo("metodo");
            Metodo();
            break;
        //default:
          //  throw new ErrorSintactico("Declaracion de metodo, constructor o atributo incorrecta en la línea  " + linea + "\nSe esperaba token <var>, <static>, <dynamic> o <identificador> y se encontro el token <"+ nombre + "," + lexema + "> ");
        }
    }

    private void Metodo()throws IOException, ErrorLexico, ErrorSintactico, ErrorSemantico {
        ModificadorMetodo mod = ModMetodo();
        Tipo tm = TipoMetodo();
        match("identificador");
        Token id = token;
        edtMetodo(token,mod,tm);        
        ArgsFormales();
        VarsLocales();
        ts.getClaseActual().getTablaMetodos().get(id.getLexema()).setAst(new AST(Bloque(),ts));
    }

    private ModificadorMetodo ModMetodo()throws IOException, ErrorLexico, ErrorSintactico {
        ModificadorMetodo mod;
        switch(nombre){
            case "static":
                match("static");
                return new ModificadorMetodo(token);
            case "dynamic":
                match("dynamic");
                return new ModificadorMetodo(token);
            default:
                throw new ErrorSintactico("Se esperaba un modificador de metodo pero se encontró el token <"+ nombre + "," + lexema + "> en la línea " + linea);
        }
    }

    private Tipo TipoMetodo()throws IOException, ErrorLexico, ErrorSintactico {
        switch(nombre){
            case "void":
                match("void");
                return new TipoPrimitivo(token);
            default:
                return Tipo();
        }    
    }

    private Tipo Tipo()throws IOException, ErrorLexico, ErrorSintactico {
        switch(nombre){
            case "identificador":
                match("identificador");
                if(!ts.getListaClases().containsKey(token.getLexema()))
                    ts.getListaClases().put(token.getLexema(), new Clase(token, true));
                return ts.getListaClases().get(token.getLexema());
            default:
                if(nombre.equals("boolean") || nombre.equals("char") || nombre.equals("int") || nombre.equals("String") )
                    return TipoPrimitivo();
                else
                   throw new ErrorSintactico("Se esperaba tipo pero se encontró el token <"+ nombre + "," + lexema + "> en la línea " + linea);
        } 
    }

    private TipoPrimitivo TipoPrimitivo()throws IOException, ErrorLexico, ErrorSintactico {
        TipoPrimitivo t;
        switch(nombre){
            case "boolean":
                match("boolean");
                return new TipoPrimitivo(token);
            case "char":
                match("char");
                return new TipoPrimitivo(token);
            case "int":
                match("int");
                return new TipoPrimitivo(token);
            case "String":
                match("String");
                return new TipoPrimitivo(token);
            default:
                throw new ErrorSintactico("Se esperaba tipo, pero se encontró el token <"+ nombre + "," + lexema + "> en la línea " + linea);
        }    
    }

    private void Atributo() throws IOException, ErrorLexico, ErrorSintactico, ErrorSemantico {
        // <Atributo> ->var <Tipo> <ListaDecVars> ;
        match("var");
        ListaDecVars(Tipo());
        match("punto_y_coma");
    }

    private void ListaDecVars(Tipo t)throws IOException, ErrorLexico, ErrorSintactico, ErrorSemantico {
        if(nombre.equals("identificador")){
            consumirToken();            
            switch(ts.getDefiniendo()){
                case "clase":
                    if(!ts.getClaseActual().contieneAtributo(token.getLexema()))
                        ts.getClaseActual().insertarAtributo(token, t);
                    else
                        throw new ErrorSemantico("La variable " + token.getLexema() + " ya se encuentra definida. Linea: " + token.getLinea());
                    break;
                case "metodo":
                    if(!ts.getMetodoActual().contieneVariable(token.getLexema()))
                        ts.getMetodoActual().insertarVariable(token, t);
                    else
                        throw new ErrorSemantico("La variable " + token.getLexema() + " ya se encuentra definida. Linea: " + token.getLinea());
                    break;
                case "constructor":
                    if(!ts.getClaseActual().getConstructor().contieneVariable(token.getLexema()))
                        ts.getClaseActual().getConstructor().insertarVariable(token, t);
                    else
                        throw new ErrorSemantico("La variable " + token.getLexema() + " ya se encuentra definida. Linea: " + token.getLinea());
            }		
            ListaDecVarsF(t);
        }
        else           
            throw new ErrorSintactico("Se esperaba <identificador> para variable, pero se encontró el token <"+ nombre + "," + lexema + "> en la línea " + linea);
    }
    
    private void ListaDecVarsF(Tipo t)throws IOException, ErrorLexico, ErrorSintactico, ErrorSemantico {
        if(nombre.equals("coma")){
            consumirToken();
            ListaDecVars(t);
        }
        else
            if (!nombre.equals("punto_y_coma")){//siguiente(ListaDecVarF)=siguiente(ListaDecVar)= ;
                 throw new ErrorSintactico("Se esperaba <punto_y_coma> pero se encontró el token <"+ nombre + "," + lexema + "> en la línea " + linea);

            }
    }

    private void Ctor()throws IOException, ErrorLexico, ErrorSintactico, ErrorSemantico {
        // <Ctor> -> identificador <ArgsFormales> <VarsLocales> <Bloque>    
           match("identificador");
           if( ts.getClaseActual().getConstructor() == null)
                ts.getClaseActual().setConstructor(new Constructor(token,ts.getClaseActual()));
            else
                throw new ErrorSemantico("Ya existe constructor para la clase " + ts.getClaseActual().getNombre() + ". Linea:" + token.getLinea());
           ArgsFormales();
           VarsLocales();
           ts.getClaseActual().getConstructor().setAst(new AST(Bloque(),ts));
       }      

    private void ArgsFormales()throws IOException, ErrorLexico, ErrorSintactico, ErrorSemantico {
        // <ArgsFormales> -> ( <ArgsFormalesF>
        if(nombre.equals("parentesis_abre")){
          consumirToken();
          ArgsFormalesF();
        }
        else 
            throw new ErrorSintactico("Se esperaba <parentesis_abre> para iniciar declaracion de argumentos formales"
            + " pero se encontró el token <"+ nombre + ", " + lexema + "> en la línea " + linea);
    }        

    private void ArgsFormalesF()throws IOException, ErrorLexico, ErrorSintactico, ErrorSemantico {
        // <ArgsFormales> -> ( <ArgsFormalesF>
        if(nombre.equals("parentesis_cierra")){
          consumirToken();
        }
        else if(nombre.equals("identificador") || nombre.equals("boolean") || nombre.equals("char") || nombre.equals("String") || nombre.equals("int")){
            ListaArgsFormales();
            match("parentesis_cierra");
        }
        else
           throw new ErrorSintactico("Se esperaba tipo del argumento formal o <parentesis_cierra> marcando el fin de los argumentos formales"
            + " pero se encontró el token <"+ nombre + ", " + lexema + "> en la línea " + linea);
    }    
    
    private void ListaArgsFormales()throws IOException, ErrorLexico, ErrorSintactico, ErrorSemantico {
        // <ListaArgsFormales> -> <ArgFormal> <ListaArgsFormalesF>
        ArgFormal();
        ListaArgsFormalesF();
    }             

    private void ArgFormal()throws IOException, ErrorLexico, ErrorSintactico, ErrorSemantico {
        // <ArgFormal> -> <Tipo> identificador
        Tipo t = Tipo();
        match("identificador");
        
        if(ts.getDefiniendo().equals("constructor"))
            if(!ts.getClaseActual().getConstructor().contieneParametro(token.getLexema()))
                ts.getClaseActual().getConstructor().insertarParametro(token, t); 
            else
                throw new ErrorSemantico("El parametro " + token.getLexema() + " ya se encuentra definida. Linea: " + token.getLinea());
        else 
            if(!ts.getMetodoActual().contieneParametro(token.getLexema()))
                ts.getMetodoActual().insertarParametro(token, t); 
            else
                throw new ErrorSemantico("El parametro " + token.getLexema() + " ya se encuentra definida. Linea: " + token.getLinea());
    }      

    private void ListaArgsFormalesF()throws IOException, ErrorLexico, ErrorSintactico, ErrorSemantico {
        //<ListaArgsFormalesF> -> , <ListaArgsFormales> | λ
        if (nombre.equals("coma")){
            consumirToken();
            ListaArgsFormales();
        }
        else if (!nombre.equals("parentesis_cierra")){  //siguiente(ListaArgsFormalesF)=siguiente(ListaArgsFormales)= )
            throw new ErrorSintactico("Se esperaba <parentesis_cierra> marcando el fin de los argumentos formales"
            + " pero se encontró el token <"+ nombre + ", " + lexema + "> en la línea " + linea);
       }
    }

    private void VarsLocales()throws IOException, ErrorLexico, ErrorSintactico, ErrorSemantico {
        //<VarsLocales> -> <ListaAtributo> | λ 
        if(nombre.equals("var"))
            ListaAtributo();
        if (!nombre.equals("llave_abre")){ //Siguiente(VarLocales)=Primero(Bloque)={
            throw new ErrorSintactico("Se esperaba declaracion de variable o <llave_abre> pero se encontró el token <"+ nombre + ", " + lexema + "> en la línea " + linea);
        }                 
    }

    private void ListaAtributo()throws IOException, ErrorLexico, ErrorSintactico, ErrorSemantico {
        Atributo();
        VarsLocales();
    }

    private Bloque Bloque()throws IOException, ErrorLexico, ErrorSintactico {
        match("llave_abre");
        List<Sentencia> list = new ArrayList();
        ListaSentencias(list);
        match("llave_cierra");
        return new Bloque(list);
    }

    private void ListaSentencias(List<Sentencia> list)throws IOException, ErrorLexico, ErrorSintactico {
       //<ListaSentencias> -> <Sentencia> <ListaSenteciasF> | λ 
        if(nombre.equals("punto_y_coma") || nombre.equals("if") || nombre.equals("while") || nombre.equals("for") || nombre.equals("identificador") || 
        nombre.equals("parentesis_abre") || nombre.equals("llave_abre") || nombre.equals("return")){            
            list.add(Sentencia());
            ListaSentenciasF(list);
        }
        else if (!nombre.equals("llave_cierra")){
            throw new ErrorSintactico("Se esperaba una sentencia, pero se encontró el token <"+ nombre + ", " + lexema + "> en la línea " + linea);
        }
    }

    private void ListaSentenciasF(List<Sentencia> list)throws IOException, ErrorLexico, ErrorSintactico {        
        if(nombre.equals("punto_y_coma") || nombre.equals("if") || nombre.equals("while") || nombre.equals("for") || nombre.equals("identificador") || 
        nombre.equals("parentesis_abre") || nombre.equals("llave_abre") || nombre.equals("return")){
            ListaSentencias(list);
        }
        else if (!nombre.equals("llave_cierra")){
            throw new ErrorSintactico("Se esperaba una sentencia, pero se encontró el token <"+ nombre + ", " + lexema + "> en la línea " + linea);
        }
    }

    private Sentencia Sentencia()throws IOException, ErrorLexico, ErrorSintactico {
        Sentencia sent = null;
        Expresion exp = null;
          switch(nombre){
              case "punto_y_coma":
                  match("punto_y_coma");
                  sent = new SentenciaVacia();
                  break;
              case "identificador":
                  sent = Asignacion();
                  match("punto_y_coma");
                  break;
              case "parentesis_abre":
                  sent = SentenciaSimple();
                  match("punto_y_coma");
                  break;
              case "if":
                  match("if");
                  Token i = token;
                  match("parentesis_abre");
                  exp = Expresion();
                  match("parentesis_cierra");
                  Sentencia aux = Sentencia();
                  Else else_aux = SentenciaF1();
                  sent = new If(i, exp, aux,else_aux);
                  break;
              case "while":
                  match("while");
                  Token w = token;
                  match("parentesis_abre");
                  exp = Expresion();
                  match("parentesis_cierra");
                  sent = Sentencia();
                  return new While(w, exp, sent, ts);
              case "for":
                  match("for");
                  Token f = token;
                  match("parentesis_abre");
                  Asignacion asig = Asignacion();
                  match("punto_y_coma");
                  exp = Expresion();
                  match("punto_y_coma");
                  Expresion exp1 = Expresion();
                  match("parentesis_cierra");
                  sent = Sentencia();
                  return new For(f,asig, exp, exp1, sent);
              case "llave_abre":
                  sent = Bloque();
                  break;
              case "return":
                  match("return");                 
                  sent = new Return(token,SentenciaF2());
                  break;
              default:
                  throw new ErrorSintactico("Se esperaba una sentencia, pero se encontró el token <"+ nombre + ", " + lexema + "> en la línea " + linea);
                     
          }
          return sent;
      }

    private Else SentenciaF1() throws IOException, ErrorLexico, ErrorSintactico{        
        if (nombre.equals("else")){
            consumirToken();
            return new Else(Sentencia());
        }
        else if(!nombre.equals("punto_y_coma") && !nombre.equals("if") && !nombre.equals("while") && !nombre.equals("for") && !nombre.equals("identificador") &&                  
        !nombre.equals("parentesis_abre") && !nombre.equals("llave_abre") && !nombre.equals("llave_cierra") && !nombre.equals("else") && !nombre.equals("return"))
            throw new ErrorSintactico("Se esperaba <else> o sentencia y se encontro el token: <" + nombre + "," + lexema + "> en la linea " + linea);
        return null;
    }

    private Expresion SentenciaF2() throws IOException, ErrorLexico, ErrorSintactico{
        if (!nombre.equals("punto_y_coma")){ 
            Expresion exp = Expresion();
            match("punto_y_coma");
            return exp;
        }
        else{
            match("punto_y_coma");
            return null;
        }
    }

    private Asignacion Asignacion() throws IOException, ErrorLexico, ErrorSintactico {
          //Regla: <Asignacion> -> identificador = <Expresion> 
          match("identificador");
          Token ladoIzq = token;
          match("asignacion");
          return new Asignacion(ladoIzq, Expresion(), token);
    }

    private SentenciaSimple SentenciaSimple() throws IOException, ErrorLexico, ErrorSintactico {
        //<SentenciaSimple> -> ( <Expresion> )
        match("parentesis_abre");
        Expresion exp = Expresion();
        match("parentesis_cierra");
        return new SentenciaSimple(exp);
    }

    private Expresion Expresion() throws IOException, ErrorLexico, ErrorSintactico {
        return ExpOr();
    }       

    private Expresion ExpOr() throws IOException, ErrorLexico, ErrorSintactico {        
        return ExpOrR(ExpAnd());
    }    

    private Expresion ExpOrR(Expresion exp) throws IOException, ErrorLexico, ErrorSintactico {
        if(nombre.equals("disyuncion")){            
            consumirToken();
            Token aux = token;
            Expresion exp1 = new ExpBinaria(exp,ExpAnd(), aux);
            return ExpOrR(exp1);
        }
        return exp;
    }

    private Expresion ExpAnd() throws IOException, ErrorLexico, ErrorSintactico {        
        return ExpAndR(ExpComparacion());
    }

    private Expresion ExpAndR(Expresion exp) throws IOException, ErrorLexico, ErrorSintactico {
        if(nombre.equals("conjuncion")){
            consumirToken();
             Token aux = token;
            Expresion exp1 = new ExpBinaria(exp,ExpComparacion(),aux);
            return ExpAndR(exp1);
        }
        return exp;
    }

    private Expresion ExpComparacion() throws IOException, ErrorLexico, ErrorSintactico {
          return ExpComparacionR(ExpMayorMenor());
      }

    private Expresion ExpComparacionR(Expresion exp) throws IOException, ErrorLexico, ErrorSintactico { 
        Expresion exp1 = null;
         Token aux = null;
        switch(nombre){            
            case "igual":
                match("igual"); 
                aux = token;
                exp1 = new ExpBinaria(exp,ExpMayorMenor(),aux);             
                return ExpComparacionR(exp1);
            case "distinto":                
                match("distinto"); 
                aux = token;
                exp1 = new ExpBinaria(exp,ExpMayorMenor(),aux);
                return ExpComparacionR(exp1); 
            default:
                return exp;               
        }        
    }

    private Expresion ExpMayorMenor() throws IOException, ErrorLexico, ErrorSintactico {       
        return ExpMayorMenorF(ExpSumaResta());
    }

    private Expresion ExpMayorMenorF(Expresion exp) throws IOException, ErrorLexico, ErrorSintactico {  
         Token aux = null;
        switch(nombre){
            case "mayor":
                match("mayor");
                aux = token;
                return new ExpBinaria(exp,ExpSumaResta(),aux);
            case "menor":
                match("menor");
                aux = token;
                return new ExpBinaria(exp,ExpSumaResta(),aux);
            case "mayor_o_igual":
                match("mayor_o_igual");
                aux = token;
                return new ExpBinaria(exp,ExpSumaResta(),aux);
            case "menor_o_igual":
                match("menor_o_igual");
                aux = token;
                return new ExpBinaria(exp,ExpSumaResta(),aux);
            default:
                return exp;
          } 
    }

    private Expresion ExpSumaResta() throws IOException, ErrorLexico, ErrorSintactico {
         return ExpSumaRestaR(ExpMultDivMod());
      }

    private Expresion ExpSumaRestaR(Expresion exp) throws IOException, ErrorLexico, ErrorSintactico {
          Expresion exp1 = null;
          Token aux;
          switch(nombre){
              case "mas":
                  match("mas");
                  aux = token;
                  exp1 = new ExpBinaria(exp,ExpMultDivMod(),aux);    
                  return ExpSumaRestaR(exp1);
              case "menos":
                  match("menos");
                  aux = token;
                  exp1 = new ExpBinaria(exp,ExpMultDivMod(),aux);   
                  return ExpSumaRestaR(exp1);
              default:
                  return exp;
          } 
    }

    private Expresion ExpMultDivMod() throws IOException, ErrorLexico, ErrorSintactico {
         return ExpMultDivModR(ExpUnaria());
    }
    
    private Expresion ExpMultDivModR(Expresion exp) throws IOException, ErrorLexico, ErrorSintactico {
        Expresion exp1 = null;  
         Token aux;
        switch(nombre){
              case "por":
                  match("por");
                  aux = token;
                  exp1 = new ExpBinaria(exp,ExpUnaria(),aux);
                  return ExpMultDivModR(exp1);
              case "division":
                  match("division");
                  aux = token;
                  exp1 = new ExpBinaria(exp,ExpUnaria(),aux);
                  return ExpMultDivModR(exp1);
              case "modulo":
                  match("modulo");
                  aux = token;
                  exp1 = new ExpBinaria(exp,ExpUnaria(),aux);
                  return ExpMultDivModR(exp1);
              default:
                  return exp;
          } 
    }
    
    private Expresion ExpUnaria() throws IOException, ErrorLexico, ErrorSintactico {
         Token aux = token; 
         switch(nombre){
                  case "not":
                      match("not");
                      aux = token;
                      return new ExpUnaria(ExpUnaria(),aux);
                  case "mas":
                      match("mas");
                      aux = token;
                      return new ExpUnaria(ExpUnaria(),aux);
                  case "menos":
                      match("menos");
                      aux = token; 
                      return new ExpUnaria(ExpUnaria(),aux); 
                  default:
                      return Primario();
              }
      }  

    private Primario Primario() throws IOException, ErrorLexico, ErrorSintactico {
        List<Llamada> list;
        Token aux;
         switch(nombre){
             case "this":
                 match("this");
                 return new This(token);
             case "parentesis_abre":
                 match("parentesis_abre");
                 Expresion exp = Expresion();
                 match("parentesis_cierra");
                 list = new ArrayList();
                 ListaLlamadas(list);
                 return new ExpLlamada(exp, list);
             case "new":
                 match("new");
                 match("identificador");
                 aux = token;
                 List<Expresion> args = ArgsActuales();
                 list = new ArrayList();
                 ListaLlamadas(list);
                 return new New(aux, args, list);
            case "identificador":
                 match("identificador");
                 aux = token;
                 Object[] p = PrimarioF();
                 return new Identificador(aux,(List<Expresion>) p[0],(List<Llamada>) p[1]);
             default:
                 return Literal();
         }
      }

    private Object[] PrimarioF() throws IOException, ErrorLexico, ErrorSintactico {
        Object[] ret = new Object[2];
         switch(nombre){
             case "punto":
                 ret[1] = ListaLlamadas(new ArrayList<Llamada>());
                 ret[0] = null;
                 break;
             case "parentesis_abre":
                 ret[0] = ArgsActuales();
                 ret[1] = ListaLlamadas(new ArrayList<Llamada>());
                 break;
         }
         return ret;
    }

    private Literal Literal() throws IOException, ErrorLexico, ErrorSintactico {
        switch(nombre){
             case "null":
                 match("null");
                 return new Null(token);
             case "true":
                 match("true");
                 return new True(token);
             case "false":
                 match("false");
                 return new False(token);
            case "numero":
                 match("numero");
                 return new IntLiteral(token);
            case "caracter":
                match("caracter");
                return new CharLiteral(token);
            case "cadena":
                match("cadena");
                return new StringLiteral(token);
            default:
                throw new ErrorSintactico("Se esperaba un literal, pero se encontró el token <"+ nombre + ", " + lexema + "> en la línea " + linea);
         }
      }

    private List<Expresion> ArgsActuales() throws IOException, ErrorLexico, ErrorSintactico {
         match("parentesis_abre");
         List<Expresion> list = new ArrayList();
         ListaExps(list);
         match("parentesis_cierra");
         return list;
     }

    private void ListaExps(List<Expresion> list) throws IOException, ErrorLexico, ErrorSintactico {
        if(nombre.equals("mas") || nombre.equals("menos") || nombre.equals("not") || nombre.equals("caracter") || nombre.equals("cadena") || 
        nombre.equals("numero") || nombre.equals("this") || nombre.equals("identificador") || nombre.equals("new") || nombre.equals("parentesis_abre")
        || nombre.equals("false") ||nombre.equals("true") ||nombre.equals("null")){
            list.add(Expresion());
            ListaExpsF(list);
        }
        else if(!nombre.equals("parentesis_cierra")){
            throw new ErrorSintactico("Se esperaba <parentesis_cierra> y se encontro el token: <" + nombre + "," + lexema + "> en la linea " + linea);
        }         
    }

    private void ListaExpsF(List<Expresion> list) throws IOException, ErrorLexico, ErrorSintactico {
        if(nombre.equals("coma")){
            consumirToken();
            ListaExps(list);
        }
        else if(!nombre.equals("parentesis_cierra"))
            throw new ErrorSintactico("Se esperaba <parentesis_cierra> y se encontro el token: <" + nombre + "," + lexema + "> en la linea " + linea);
     }

    private List<Llamada> ListaLlamadas(List<Llamada> list) throws IOException, ErrorLexico, ErrorSintactico {
        if(nombre.equals("punto")){
            list.add(Llamada());
            ListaLlamadasF(list);            
        }
        else if(!nombre.equals("mas") && !nombre.equals("menos") && !nombre.equals("division") && !nombre.equals("por") && !nombre.equals("mayor") &&                   // Si no es el
        !nombre.equals("menor") && !nombre.equals("mayor_o_igual") && !nombre.equals("menor_o_igual") && !nombre.equals("not") &&                                       // SIGUIENTE(ListaLlamadas)           
        !nombre.equals("modulo") && !nombre.equals("igual") && !nombre.equals("distinto") && !nombre.equals("conjuncion") && !nombre.equals("disyuncion")
        && !nombre.equals("parentesis_cierra") && !nombre.equals("punto_y_coma") && !nombre.equals("coma"))              // Error.           
            throw new ErrorSintactico("Se esperaba un operador y se encontro el token: <" + nombre + "," + lexema + "> en la linea " + linea);
        
        return list;
    }

    private Llamada Llamada() throws IOException, ErrorLexico, ErrorSintactico {
      match("punto");
      match("identificador");
      Token aux = token;
      return new Llamada(aux, ArgsActuales());
    }

    private void ListaLlamadasF(List<Llamada> list) throws IOException, ErrorLexico, ErrorSintactico {
        ListaLlamadas(list);
    }

    private void edtInicial() throws ErrorSemantico{ 
        ts = new TablaDeSimbolos();
    
        Clase obj = new Clase(new Token("identificador", "Object", -1),false);
        obj.setCompleta(true);
        ts.getListaClases().put("Object",obj);
        Clase system = new Clase(new Token("identificador", "System", -1),false);
        system.setCompleta(true);
        
        TipoPrimitivo t = new TipoPrimitivo(new Token("identificador", "int", -1));
        Metodo metodo = new Metodo(new Token("identificador", "read", -1),t,new ModificadorMetodo(new Token("identificador", "static", -1)),system);
        system.insertarMetodo("read", metodo);
        
        t = new TipoPrimitivo(new Token("void", "void", -1));
        
        metodo = new Metodo(new Token("identificador", "printB", -1),t,new ModificadorMetodo(new Token("identificador", "static", -1)),system);
        t = new TipoPrimitivo(new Token("boolean", "boolean", -1));
        metodo.insertarParametro(new Token("identificador", "b", -1),t);
        system.insertarMetodo("printB",metodo);

        t = new TipoPrimitivo(new Token("void", "void", -1));
        metodo = new Metodo(new Token("identificador", "printC", -1),t,new ModificadorMetodo(new Token("static", "static", -1)),system);
        t = new TipoPrimitivo(new Token("char", "char", -1));
        metodo.insertarParametro(new Token("identificador", "c", -1),t);							 
        system.insertarMetodo("printC", metodo);

        t = new TipoPrimitivo(new Token("void", "void", -1));
         metodo = new Metodo(new Token("identificador", "printI", -1),t,new ModificadorMetodo(new Token("static", "static", -1)),system);
         t = new TipoPrimitivo(new Token("int", "int", -1));
        metodo.insertarParametro(new Token("identificador", "i", -1),t);
        system.insertarMetodo("printI",metodo);

        t = new TipoPrimitivo(new Token("void", "void", -1));
        metodo = new Metodo(new Token("identificador", "printS", -1),t,new ModificadorMetodo(new Token("static", "static", -1)),system);
        t = new TipoPrimitivo(new Token("String", "String", -1));
        metodo.insertarParametro(new Token("identificador", "s", -1),t);							 
        system.insertarMetodo("printS", metodo);
        
        metodo = new Metodo(new Token("identificador", "printBln", -1),t,new ModificadorMetodo(new Token("identificador", "static", -1)),system);
        t = new TipoPrimitivo(new Token("boolean", "boolean", -1));
        metodo.insertarParametro(new Token("identificador", "b", -1),t);
        system.insertarMetodo("printBln",metodo);

        t = new TipoPrimitivo(new Token("void", "void", -1));
        metodo = new Metodo(new Token("identificador", "printCln", -1),t,new ModificadorMetodo(new Token("static", "static", -1)),system);
        t = new TipoPrimitivo(new Token("char", "char", -1));
        metodo.insertarParametro(new Token("identificador", "c", -1),t);							 
        system.insertarMetodo("printCln", metodo);

        t = new TipoPrimitivo(new Token("void", "void", -1));
         metodo = new Metodo(new Token("identificador", "printIln", -1),t,new ModificadorMetodo(new Token("static", "static", -1)),system);
         t = new TipoPrimitivo(new Token("int", "int", -1));
        metodo.insertarParametro(new Token("identificador", "i", -1),t);
        system.insertarMetodo("printIln",metodo);

        t = new TipoPrimitivo(new Token("void", "void", -1));
        metodo = new Metodo(new Token("identificador", "printSln", -1),t,new ModificadorMetodo(new Token("static", "static", -1)),system);
        t = new TipoPrimitivo(new Token("String", "String", -1));
        metodo.insertarParametro(new Token("identificador", "s", -1),t);							 
        system.insertarMetodo("printSln", metodo);
        
        t = new TipoPrimitivo(new Token("void", "void", -1));
        metodo = new Metodo(new Token("identificador", "println", -1),t,new ModificadorMetodo(new Token("static", "static", -1)),system);        							 
        system.insertarMetodo("println", metodo);

        system.setPadre(obj);

        ts.getListaClases().put("System",system);
    }

    private Clase edtClase(Token id) throws ErrorSemantico {
        if(!ts.getListaClases().containsKey(id.getLexema())){
            Clase clase = new Clase(id, false);
            ts.setClaseActual(clase);
            ts.getListaClases().put(id.getLexema(), clase);
            return clase;
        }
        else if(ts.getListaClases().get(id.getLexema()).pendiente()){
            ts.setClaseActual(ts.getListaClases().get(id.getLexema()));
            ts.getClaseActual().setPendiente(false);
            return ts.getClaseActual();
        }
        else            
            throw new ErrorSemantico("La clase "+ id.getLexema() + " ya se encuentra definida. Linea: " + id.getLinea());
    }

    private void edtMetodo(Token id,ModificadorMetodo mod,Tipo t) throws ErrorSemantico {       
        Metodo m = ts.getClaseActual().getTablaMetodos().get(id.getLexema());       
        if(m == null){
            m = new Metodo(id, t, mod,ts.getClaseActual());
            ts.getClaseActual().getTablaMetodos().put(id.getLexema(),m);
            ts.setMetodoActual(m);
        }
        else
            throw new ErrorSemantico ("Ya existe metodo con el nombre "+m.getNombre()+". Linea: " + id.getLinea());
    }


}
